<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="mobile-web-app-capable" content="yes" />
    <title>智能助手</title>
    <script src="js/vue.global.prod.js"></script>
    <script src="js/axios.min.js"></script>
    <link rel="stylesheet" href="js/github.min.css" />
    <script src="js/marked.min.js"></script>
    <script src="js/highlight.min.js"></script>
    <script>
    axios.defaults.withCredentials = true;
    </script>
    <style>
        :root {
            --primary-color: #2b68e8;
            --bg-color: #f4f6f9;
            --fg-color: #ffffff;
            --text-primary: #1f2329;
            --text-secondary: #646a73;
        }
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;
            background: var(--bg-color);
            -webkit-font-smoothing: antialiased;
            -moz-osx-font-smoothing: grayscale;
            color: var(--text-primary);
        }
        html, body {
            height: 100vh;
            margin: 0;
            padding: 0;
        }
        #app {
            width: 100vw;
            height: 100vh;
            margin: 0;
            background: var(--fg-color);
            display: flex;
            flex-direction: column;
        }
        .header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 12px 16px;
            background: var(--fg-color);
            border-bottom: 1px solid #e5e6eb;
            flex-shrink: 0;
        }
        .header-title {
            font-weight: 600;
            font-size: 17px;
        }
        .header-actions button {
            background: none;
            border: none;
            font-size: 24px;
            cursor: pointer;
            color: var(--text-primary);
        }
        .main {
            flex: 1;
            display: flex;
            flex-direction: column;
            overflow: hidden;
            background: var(--bg-color);
            min-height: 0;
        }
        .chat-history {
            flex: 1;
            overflow-y: auto;
            padding: 16px;
            min-height: 0;
        }
        .chat-item {
            margin-bottom: 24px;
            display: flex;
            max-width: 85%;
        }
        .chat-item.user {
            margin-left: auto;
            flex-direction: row-reverse;
        }
        .chat-bubble {
            padding: 10px 16px;
            border-radius: 20px;
            line-height: 1.6;
            word-break: break-word;
            font-size: 16px;
        }
        .chat-item.user .chat-bubble {
            background: var(--primary-color);
            color: #fff;
            border-bottom-right-radius: 6px;
        }
        .chat-item.assistant .chat-bubble {
            background: var(--fg-color);
            color: var(--text-primary);
            border: 1px solid #e5e6eb;
            border-bottom-left-radius: 6px;
        }
        .input-row {
            padding: 12px 16px;
            display: flex;
            gap: 10px;
            background: var(--fg-color);
            border-top: 1px solid #e5e6eb;
            flex-shrink: 0;
            align-items: center;
        }
        .input-row input {
            flex: 1;
            padding: 10px 16px;
            border-radius: 22px;
            border: none;
            background: var(--bg-color);
            font-size: 16px;
            color: var(--text-primary);
        }
        .input-row input:focus {
            outline: none;
        }
        .input-row button {
            width: 36px;
            height: 36px;
            border-radius: 50%;
            border: none;
            background: var(--primary-color);
            color: #fff;
            font-size: 20px;
            cursor: pointer;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        .input-row button:disabled {
            background: #b0bec5;
            cursor: not-allowed;
        }
        /* Auth Modal Styles */
       .auth-modal {
         position: fixed; z-index: 1000;
         left: 0; top: 0; right: 0; bottom: 0;
         background: rgba(0,0,0,0.4);
         display: flex; align-items: center; justify-content: center;
       }
       .auth-box {
         background: #fff; border-radius: 12px;
         padding: 24px; width: 90%; max-width: 350px;
       }
       .auth-box h2 { text-align: center; color: var(--primary-color); margin-bottom: 20px; }
       .auth-box input { width: 100%; padding: 12px; border-radius: 6px; border: 1px solid #ddd; margin-bottom: 12px; box-sizing: border-box; }
       .auth-box button { width: 100%; padding: 12px; border: none; border-radius: 6px; background: var(--primary-color); color: white; font-size: 16px; cursor: pointer; }
       .auth-box a { display: block; text-align: center; margin-top: 12px; color: var(--primary-color); }
       .close-btn { position: absolute; top: 10px; right: 10px; cursor: pointer; font-size: 24px; color: #aaa; }
        .markdown-body img {
            max-width: 70vw;
            max-height: 220px;
            border-radius: 10px;
            box-shadow: 0 2px 8px #0001;
            display: block;
            margin: 8px 0;
            object-fit: contain;
        }
    </style>
</head>
<body>
    <div id="app">
        <header class="header">
            <button class="back-btn">‹</button>
            <div class="header-title" v-if="user && selectedDomainName">{{ selectedDomainName }}</div>
            <div class="header-title" v-else>智能助手</div>
            <div class="header-actions">
                <span v-if="user" style="font-size:14px;margin-right:10px;">{{ user.name }}</span>
                <button @click="showLogin = true" v-if="!user">登录</button>
                <button @click="logout" v-else>退出</button>
            </div>
        </header>

        <!-- 领域选择器 -->
        <div v-if="user && domains.length" style="padding: 8px 16px 0 16px; background: var(--fg-color);">
            <select v-model="selectedDomain" @change="onDomainChange" title="选择领域" style="width:100%;padding:8px 10px;border-radius:6px;border:1px solid #e5e6eb;font-size:16px;">
                <option v-for="d in domains" :value="d.id" :key="d.id">{{ d.name }}</option>
            </select>
        </div>

        <!-- 历史聊天列表 -->
        <div v-if="user && conversations.length" style="padding: 8px 16px 0 16px; background: var(--fg-color);overflow-x:auto;white-space:nowrap;position:relative;">
            <span @click="selectConversation('')"
                :style="{
                    display: 'inline-block',
                    padding: '6px 12px',
                    borderRadius: '16px',
                    background: !selectedConversation ? '#e3f2fd' : '#f4f6f9',
                    border: !selectedConversation ? '1.5px solid #1976d2' : '1px solid #e5e6eb',
                    color: !selectedConversation ? '#1976d2' : '#333',
                    cursor: 'pointer',
                    fontWeight: !selectedConversation ? 'bold' : 'normal',
                    position: 'sticky',
                    left: 0,
                    zIndex: 2,
                    backgroundClip: 'padding-box',
                    marginRight: '8px',
                    boxShadow: '2px 0 4px -2px #eee'
                }">
                新会话
            </span>
            <span v-for="c in filteredConversations" :key="c.id" @click="selectConversation(c.id)" :style="{display:'inline-block',padding:'6px 12px',marginRight:'8px',borderRadius:'16px',background:c.id===selectedConversation?'#e3f2fd':'#f4f6f9',border:c.id===selectedConversation?'1.5px solid #1976d2':'1px solid #e5e6eb',color:c.id===selectedConversation?'#1976d2':'#333',cursor:'pointer',fontWeight:c.id===selectedConversation?'bold':'normal'}">
                {{ c.label }}
            </span>
        </div>

        <div class="main">
            <div class="chat-history" ref="chatHistory">
                <div v-for="(item, index) in messages" :key="index" :class="['chat-item', item.role]">
                    <div class="chat-bubble markdown-body" v-html="renderMarkdown(item.content)"></div>
                </div>
                <div v-if="error" class="chat-item assistant">
                    <div class="chat-bubble" style="color: red;">{{ error }}</div>
                </div>
            </div>
        </div>

        <div class="input-row">
            <input type="file" ref="imageInput" accept="image/*" style="display:none" @change="handleImageSelect" />
            <button @click="$refs.imageInput.click()" :disabled="loading || !user" title="发送图片" style="font-size:20px;">📷</button>
            <input type="text" v-model="question" placeholder="发消息..." @keyup.enter="ask" :disabled="loading || !user" />
            <button @click="startVoiceInput" :disabled="!voiceSupported || loading || !user" :style="voiceRecording ? 'background:#ff9800;color:#fff;' : ''" title="语音输入">
                <span v-if="!voiceRecording">🎤</span>
                <span v-else>■</span>
            </button>
            <button @click="ask" :disabled="loading || (!question.trim() && !pendingImageBase64) || !user">+</button>
        </div>
        <!-- 显示已选图片缩略图 -->
        <div v-if="pendingImageUrl" style="padding: 0 16px 8px 16px;">
            <div style="position:relative;display:inline-block;">
                <img :src="pendingImageUrl" alt="预览图片" style="max-width: 120px; max-height: 120px; border-radius: 8px; box-shadow: 0 2px 8px #0001;" />
                <span @click="removePendingImage" style="position:absolute;top:2px;right:2px;background:#fff;border-radius:50%;padding:2px 6px;cursor:pointer;font-size:16px;box-shadow:0 1px 4px #0002;">×</span>
            </div>
        </div>

        <div v-if="showLogin" class="auth-modal">
            <div class="auth-box">
                <span @click="showLogin = false" class="close-btn" style="position: relative; float: right; top: -20px; right: -10px;">&times;</span>
                <h2>{{ isRegistering ? '注册' : '登录' }}</h2>
                <input type="text" v-model="username" placeholder="用户名" />
                <input type="password" v-model="password" placeholder="密码" />
                <button @click="isRegistering ? register() : login()">{{ isRegistering ? '注册' : '登录' }}</button>
                <a href="#" @click.prevent="isRegistering = !isRegistering">
                    {{ isRegistering ? '已有账户? 去登录' : '没有账户? 去注册' }}
                </a>
            </div>
        </div>
    </div>

    <script>
        const { createApp, ref, onMounted, nextTick, computed } = Vue;

        createApp({
            setup() {
                const question = ref('');
                const messages = ref([]);
                const error = ref('');
                const loading = ref(false);
                const chatHistory = ref(null);
                const domains = ref([]);
                const selectedDomain = ref(null);

                const user = ref(null);
                const showLogin = ref(false);
                const isRegistering = ref(false);
                const username = ref('');
                const password = ref('');

                // 新增：历史聊天相关
                const conversations = ref([]);
                const selectedConversation = ref('');
                const filteredConversations = computed(() => {
                    return conversations.value
                        .filter((c) => c.domain === selectedDomain.value)
                        .map((c) => {
                            let firstUserMsg = '';
                            if (c.messages) {
                                try {
                                    const arr = JSON.parse(c.messages);
                                    const first = arr.find((m) => m.type?.toLowerCase() === 'user');
                                    if (first) firstUserMsg = first.content;
                                } catch {}
                            }
                            return { id: c.id, label: firstUserMsg || '(无内容)', domain: c.domain };
                        });
                });

                const selectedDomainName = computed(() => {
                    const domain = domains.value.find(d => d.id === selectedDomain.value);
                    return domain ? domain.name : '医疗'; // Default name
                });

                const renderMarkdown = (text) => {
                    if (typeof text !== 'string') return '';
                    return marked.parse(text, {
                        highlight: function (code, lang) {
                            const language = hljs.getLanguage(lang) ? lang : 'plaintext';
                            return hljs.highlight(code, { language }).value;
                        },
                        gfm: true,
                        breaks: true,
                    });
                };

                const scrollToBottom = () => {
                    nextTick(() => {
                        if (chatHistory.value) {
                            chatHistory.value.scrollTop = chatHistory.value.scrollHeight;
                        }
                    });
                };

                const fetchDomains = async () => {
                    if(!user.value) return;
                    try {
                        const response = await axios.get('/account/mydomains');
                        domains.value = response.data;
                        if (domains.value.length > 0 && !selectedDomain.value) {
                            selectedDomain.value = domains.value[0].id;
                        }
                    } catch (e) {
                        error.value = '无法加载领域列表: ' + (e.response?.data || e.message);
                    }
                };
                
                const ask = async () => {
                    if ((!question.value.trim() && !pendingImageBase64.value) || loading.value || !selectedDomain.value) return;
                    let content = '';
                    if (pendingImageBase64.value) {
                        content += `![图片](base64)`;
                    }
                    if (question.value.trim()) {
                        content += question.value.trim();
                    }
                    const userMessage = { role: 'user', content, imageBase64: pendingImageBase64.value };
                    messages.value.push(userMessage);
                    const currentContent = question.value.trim();
                    const currentImageBase64 = pendingImageBase64.value;
                    question.value = '';
                    removePendingImage();
                    loading.value = true;
                    error.value = '';
                    scrollToBottom();
                    // 先插入"我正在思考..."气泡
                    const thinkingMessage = { role: 'assistant', content: '我正在思考...' };
                    messages.value.push(thinkingMessage);
                    const thinkingIndex = messages.value.length - 1;

                    try {
                        const data = {
                            question: currentContent,
                            domain: selectedDomain.value
                        };
                        if (selectedConversation.value) {
                            data.cid = selectedConversation.value;
                        }
                        if (currentImageBase64) {
                            data.imageBase64 = currentImageBase64;
                        }
                        const response = await axios.post('/ask', data);
                        let reply = response.data;
                        if (reply && typeof reply === 'object' && 'answer' in reply) {
                            reply = reply.answer;
                        }
                        messages.value[thinkingIndex].content = reply;
                        await fetchConversations();
                        if (!selectedConversation.value) {
                            const last = conversations.value.filter((c) => c.domain === selectedDomain.value).slice(-1)[0];
                            if (last) selectedConversation.value = last.id;
                        }
                    } catch (e) {
                        messages.value[thinkingIndex].content = e.response?.data || '请求失败，请稍后再试。';
                    } finally {
                        loading.value = false;
                        scrollToBottom();
                    }
                };

                // 新增：本地登录状态
                const localIsLoggedIn = localStorage.getItem('isLoggedIn') === '1';
                const localUsername = localStorage.getItem('username') || '';
                if (localIsLoggedIn && localUsername) {
                    user.value = { name: localUsername };
                }

                const checkAuth = async () => {
                    try {
                        const response = await axios.get('/api/auth/me');
                        if (response.data && response.data.name) {
                            user.value = response.data;
                            localStorage.setItem('isLoggedIn', '1');
                            localStorage.setItem('username', response.data.name);
                            await fetchDomains();
                            await fetchConversations();
                        } else {
                            user.value = null;
                            localStorage.removeItem('isLoggedIn');
                            localStorage.removeItem('username');
                        }
                    } catch (e) {
                        user.value = null;
                        localStorage.removeItem('isLoggedIn');
                        localStorage.removeItem('username');
                    }
                };

                const login = async () => {
                    try {
                        const response = await axios.post('/login', null, {
                            params: {
                                username: username.value,
                                password: password.value,
                            }
                        });
                        user.value = response.data;
                        showLogin.value = false;
                        username.value = '';
                        password.value = '';
                        localStorage.setItem('isLoggedIn', '1');
                        localStorage.setItem('username', response.data.name || response.data.username || '');
                        await fetchDomains();
                        await fetchConversations();
                    } catch (e) {
                        alert('登录失败: ' + (e.response?.data || e.message));
                    }
                };

                const register = async () => {
                    try {
                        await axios.post('/api/auth/register', {
                            name: username.value,
                            password: password.value,
                        });
                        alert('注册成功，请登录');
                        isRegistering.value = false;
                    } catch (e) {
                        alert('注册失败: ' + (e.response?.data || e.message));
                    }
                };
                
                const logout = async () => {
                    user.value = null;
                    domains.value = [];
                    messages.value = [];
                    selectedDomain.value = null;
                    conversations.value = [];
                    selectedConversation.value = '';
                    localStorage.removeItem('isLoggedIn');
                    localStorage.removeItem('username');
                    location.reload();
                };

                // 新增：加载历史聊天
                const fetchConversations = async () => {
                    if (!user.value) return;
                    try {
                        const res = await axios.get('/conversations');
                        conversations.value = res.data;
                    } catch {}
                };

                // 新增：切换历史聊天
                const selectConversation = (id) => {
                    selectedConversation.value = id;
                    loadConversation();
                };

                // 新增：切换领域时清空会话
                const onDomainChange = () => {
                    selectedConversation.value = '';
                    messages.value = [];
                };

                // 新增：加载选中会话内容
                const loadConversation = () => {
                    messages.value = [];
                    if (!selectedConversation.value) return;
                    const conv = conversations.value.find((c) => c.id === selectedConversation.value);
                    if (conv?.messages) {
                        try {
                            const arr = JSON.parse(conv.messages);
                            messages.value = arr.map((m) => ({
                                role: m.type ? m.type.toLowerCase() : '',
                                content: m.content
                            }));
                        } catch {}
                    }
                };

                // 语音输入相关
                const voiceSupported = 'webkitSpeechRecognition' in window || 'SpeechRecognition' in window;
                const voiceRecording = ref(false);
                let recognition = null;

                const startVoiceInput = () => {
                    if (!voiceSupported || voiceRecording.value) return;
                    const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
                    recognition = new SpeechRecognition();
                    recognition.lang = 'zh-CN';
                    recognition.interimResults = false;
                    recognition.maxAlternatives = 1;
                    voiceRecording.value = true;
                    try {
                        recognition.start();
                    } catch (e) {
                        voiceRecording.value = false;
                        alert('语音识别启动失败：' + e.message);
                        return;
                    }
                    recognition.onresult = (event) => {
                        const transcript = event.results[0][0].transcript;
                        question.value += transcript;
                    };
                    recognition.onerror = (event) => {
                        voiceRecording.value = false;
                        if (event.error === 'not-allowed') {
                            alert('语音识别失败: 未获得麦克风权限或浏览器限制。\n请检查浏览器麦克风权限，并建议使用 HTTPS 或本地环境。');
                        } else {
                            alert('语音识别失败: ' + event.error);
                        }
                    };
                    recognition.onend = () => {
                        voiceRecording.value = false;
                    };
                };

                // 图片+文字混合消息支持（base64实现）
                const pendingImageUrl = ref("");
                const pendingImageBase64 = ref("");
                const imageInput = ref(null);
                const handleImageSelect = async (e) => {
                    const file = e.target.files[0];
                    if (!file) return;
                    // 用 FileReader 读取为 base64
                    const reader = new FileReader();
                    reader.onload = (evt) => {
                        pendingImageBase64.value = evt.target.result;
                        pendingImageUrl.value = evt.target.result;
                    };
                    reader.readAsDataURL(file);
                    e.target.value = '';
                };
                const removePendingImage = () => {
                    pendingImageUrl.value = "";
                    pendingImageBase64.value = "";
                };

                onMounted(() => {
                    checkAuth();
                });

                return {
                    question, messages, error, loading, chatHistory,
                    domains, selectedDomain, selectedDomainName,
                    ask, renderMarkdown,
                    user, showLogin, isRegistering, username, password,
                    login, logout, register,
                    conversations, selectedConversation, filteredConversations,
                    fetchConversations, selectConversation, onDomainChange, loadConversation,
                    voiceSupported, voiceRecording, startVoiceInput,
                    imageInput, handleImageSelect, pendingImageUrl, removePendingImage, pendingImageBase64,
                };
            }
        }).mount('#app');
    </script>
</body>
</html>